/* reduce */
function reduceDemo() {
  const arr = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
  console.log(
    arr.reduce(
      // reduceHandler
      (pv, cv, index) => {
        console.log(index, cv, pv);
        return pv + cv;
      }

      // 初始值
    )
  );

  // console.log(
  //     arr.reduce(
  //         (pv,cv,index)=>{
  //             console.log(index,cv,pv);
  //             return pv+cv
  //         },
  //         100
  //     )
  // );
  console.log(arr.reduce((pv, cv) => pv + cv, 100));
}
// reduceDemo()

/* 
闭包：
概念：暂未释放的一个函数执行空间
作用：被用于存储私有数据，不污染外界，避免命名冲突
典型用法：闭包内存数据，对外返回该数据的操作API; 函数式编程中作为高阶函数去加工一个入参的函数
注意事项：闭包持续不释放会形成一定的内存压力，用完了以后对其返回值置空以释放闭包，避免内存泄露
*/

/* 
函数式编程
概念：基于纯函数+闭包，实现函数的自由组合与变换
作用：函数的可复用性高，复杂的业务逻辑拆解为不同环节的流水线作业，便于阅读与维护
典型用法：组合、管道、柯里化、Promise化、函数缓存、只执行一次的函数...
注意事项：闭包+函数嵌套+递归 频繁切换上下文/作用域 时间空间开销均比较大 性能不如命令式编程
*/

/* 手撸观察者模式 */
function observerDemo() {
  class Observable {
    /* 构造函数：初始化观察者列表 */
    constructor(name) {
      this.name = name;
      this.observers = [];
    }
    /* 注册观察者 */
    register(ob) {
      this.observers.push(ob);

      //让观察者持有当前被观察者对象
      ob.observable = this;
    }
    /* 注销观察者 */
    unregister(ob) {
      this.observers = this.observers.filter((o) => o !== ob);
    }
    /* 发布事件 */
    emit(event) {
      // 让所有观察者都来响应之
      this.observers.forEach((ob) => ob.onEvent(event));
    }
  }
  class Observer {
    /* 创建实例时赋予一个name属性 */
    constructor(name) {
      this.name = name;
    }

    /* 响应事件 */
    onEvent(event) {
      console.log(this.name, "响应事件", event);
    }
  }
  class Lottery extends Observable {
    /* 彩票需要一个周期性定时器属性 */
    constructor(name) {
      // 继承父类属性name
      super(name); //原型写法Observable.call(this,name)
      this.timer = null;
    }
    /* 自动运行 */
    start() {
      if (!this.timer) {
        this.timer = setInterval(() => {
          // 发布开奖事件
          const code = parseInt(Math.random() * 3);
          console.log(this.name, "发布开奖事件", code);
          this.emit({ type: "开奖", code });

          // 发布开售事件
          this.emit({ type: "开售" });
        }, 2000);
      }
    }
    /* 停止运行 */
    stop() {
      if (this.timer) {
        clearInterval(this.timer);
        console.log(this.name, "已停止");
        this.timer = null;
      }
    }
  }
  class Player extends Observer {
    /* 玩家有注单属性 */
    constructor(name) {
      // 继承父类的name属性
      super(name);
      this.code = null;
    }
    /* 下注 */
    buy() {
      this.code = parseInt(Math.random() * 3);
      console.log(this.name, "下注", this.code);
    }
    /* 兑奖 */
    check(event) {
      const isLucky = this.code === event.code;
      console.log(this.name, isLucky ? "中奖了" : "未中奖");

      // 让彩票机停止（需要事先持有彩票机对象）（在观察它的时候就拿到它的对象）
      isLucky && this.observable.stop();
    }

    /* 个性化的响应方式：覆写override父类的onEvent */
    onEvent(event) {
      super.onEvent(event);

      switch (event.type) {
        /* 遇到开售就下注 */
        case "开售":
          this.buy();
          break;

        /* 遇到开奖就兑奖 */
        case "开奖":
          this.check(event);
          break;

        default:
          break;
      }
      console.log("");
    }
  }

  /* 业务逻辑 */
  ~(function main() {
    const lot = new Lottery("六合彩");
    const tiger = new Player("打老虎");
    const gaojin = new Player("高进");

    lot.register(tiger);
    lot.register(gaojin);
    lot.unregister(gaojin);

    lot.start();
  })();
}
observerDemo();
